import { nextTick } from 'vue';

/** SSE客户端配置选项 */
interface SSEClientOptions {
  /** 最大重连尝试次数 */
  maxReconnectAttempts?: number;
  /** 重连延迟（毫秒） */
  reconnectDelay?: number;
  /** 请求头 */
  headers?: Record<string, string>;
  /** 是否自动重连 */
  autoReconnect?: boolean;
}

/** SSE客户端事件处理器映射 */
interface SSEEventHandlers {
  [eventName: string]: (data: any) => void;
}

/** SSE连接状态 */
enum SSEConnectionStatus {
  DISCONNECTED = 'disconnected',
  CONNECTING = 'connecting',
  CONNECTED = 'connected',
  ERROR = 'error'
}

/** 通用SSE客户端类 用于建立和管理Server-Sent Events连接 */
export class SSEClient {
  private url: string;
  private options: Required<SSEClientOptions>;
  private eventSource: EventSource | null = null;
  private reconnectAttempts = 0;
  private eventHandlers: SSEEventHandlers = {};
  private status = SSEConnectionStatus.DISCONNECTED;
  private onStatusChange?: (status: SSEConnectionStatus) => void;

  /**
   * 构造函数
   *
   * @param url SSE端点URL
   * @param options 配置选项
   */
  constructor(url: string, options: SSEClientOptions = {}) {
    this.url = url;
    this.options = {
      maxReconnectAttempts: options.maxReconnectAttempts || 5,
      reconnectDelay: options.reconnectDelay || 3000,
      headers: options.headers || {},
      autoReconnect: options.autoReconnect !== undefined ? options.autoReconnect : true
    };
  }

  /** 获取当前连接状态 */
  getConnectionStatus(): SSEConnectionStatus {
    return this.status;
  }

  /** 设置连接状态变化回调 */
  setStatusChangeListener(callback: (status: SSEConnectionStatus) => void): void {
    this.onStatusChange = callback;
  }

  /**
   * 添加事件处理器
   *
   * @param eventName 事件名称
   * @param handler 事件处理器
   */
  on(eventName: string, handler: (data: any) => void): void {
    this.eventHandlers[eventName] = handler;
  }

  /** 建立SSE连接 */
  connect(): void {
    if (this.eventSource) {
      this.eventSource.close();
    }

    this.status = SSEConnectionStatus.CONNECTING;
    this.notifyStatusChange();

    // 创建EventSource
    this.eventSource = new EventSource(this.url);

    // 设置事件监听器
    this.eventSource.onopen = () => {
      this.status = SSEConnectionStatus.CONNECTED;
      this.reconnectAttempts = 0;
      this.notifyStatusChange();
      console.log(`SSE连接已建立`);
    };

    this.eventSource.onerror = () => {
      this.status = SSEConnectionStatus.ERROR;
      this.notifyStatusChange();
      console.error(`SSE连接错误`);

      if (this.options.autoReconnect && this.reconnectAttempts < this.options.maxReconnectAttempts) {
        setTimeout(() => {
          this.reconnectAttempts += 1;
          console.log(`尝试重连SSE (${this.reconnectAttempts}/${this.options.maxReconnectAttempts}): ${this.url}`);
          this.connect();
        }, this.options.reconnectDelay);
      } else if (!this.options.autoReconnect || this.reconnectAttempts >= this.options.maxReconnectAttempts) {
        this.status = SSEConnectionStatus.DISCONNECTED;
        this.notifyStatusChange();
        console.error(`SSE重连失败，已停止尝试`);
      }
    };

    // 设置自定义事件监听器
    Object.keys(this.eventHandlers).forEach(eventName => {
      if (this.eventSource) {
        this.eventSource.addEventListener(eventName, (event: MessageEvent) => {
          try {
            const data = JSON.parse(event.data);
            this.eventHandlers[eventName](data);
          } catch (error) {
            console.error(`解析SSE事件数据错误 (${eventName}):`, error);
            console.error(`原始数据: ${event.data}`);
          }
        });
      }
    });
  }

  /** 断开SSE连接 */
  disconnect(): void {
    if (this.eventSource) {
      this.eventSource.close();
      this.eventSource = null;
    }
    this.status = SSEConnectionStatus.DISCONNECTED;
    this.notifyStatusChange();
    console.log(`SSE连接已断开: ${this.url}`);
  }

  /** 通知状态变化 */
  private notifyStatusChange(): void {
    nextTick(() => {
      if (this.onStatusChange) {
        this.onStatusChange(this.status);
      }
    });
  }
}

/**
 * 创建SSE客户端实例
 *
 * @param url SSE端点URL
 * @param options 配置选项
 * @returns SSE客户端实例
 */
export function createSSEClient(url: string, options: SSEClientOptions = {}): SSEClient {
  return new SSEClient(url, options);
}
